﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace  LINQToolKit.Collections
{
    public interface IQuerySession : IEnumerable, IDisposable
    {
        IEnumerable Inner { get; }
    }

    /// <summary>
    /// Represents the abstract class for the collections doing the on-demand loading
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class QuerySession<T> : IEnumerable<T>, IEnumerator<T>, IQuerySession
    {
        private IList<T> _innerList;
        private int _pointer;

        public QuerySession()
        {
            this._pointer = -1;
            this._innerList = new List<T>();
        }

        public QuerySession(IEnumerable<T> inner) : this()
        {
            if (inner != null)
            {
                this._innerList = this._innerList.Concat(inner.ToList()).ToList();
            }
        }

        IEnumerable IQuerySession.Inner
        {
            get { return this.Inner; }
        }

        public IList<T> Inner
        {
            get { return this._innerList; }
        }

        protected abstract IEnumerable<T> LoadData();

        public abstract bool IsCompleted {get;}

        public virtual void Dispose()
        {
            // do nothing
        }

        public IEnumerator<T> GetEnumerator()
        {
            this.Reset();
            return this;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public T Current
        {
            get 
            {
                if (this._pointer < 0)
                {
                    return default(T);
                }

                return this.Inner[Math.Min(this.Inner.Count - 1, this._pointer)];
            }
        }

        object IEnumerator.Current
        {
            get { return this.Current; }
        }

        public bool MoveNext()
        {
            this._pointer++;

            if (this.Inner.Count <= this._pointer && !this.IsCompleted)
            {
                // load data
                lock (this)
                {
                    if (this.Inner.Count <= this._pointer && !this.IsCompleted)
                    {
                        var data = this.LoadData();

                        if (data != null)
                        {
                            this._innerList = this.Inner.Concat(data).ToList();
                        }
                    }
                }
            }

            return this._pointer < this.Inner.Count;
        }

        public void Reset()
        {
            this._pointer = -1;
        }
    }
}
